home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 May: Tool Chest / Developer CD Series May 1996 (Tool Chest) (Apple Computer) (1996).iso / Tool Chest / Devices / Qwertytunes / Qwertytunes.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-03  |  28.8 KB  |  1,582 lines  |  [TEXT/KAHL]

  1. /*
  2.  * file: Qwertytunes.c
  3.  *
  4.  * started 22 January 1992
  5.  * david van brink
  6.  *
  7.  */
  8.  
  9.  
  10. /*--------------------------
  11.     Inclusions
  12. --------------------------*/
  13.  
  14. #include <QuickDraw.h>
  15. #include <GestaltEqu.h>
  16. #include <Windows.h>
  17. #include <OSEvents.h>
  18. #include <Memory.h>
  19. #include <Packages.h>
  20. #include <Midi.h>
  21.  
  22. #include "BigEasy2.h"
  23. #include "BigEasyTextish.h"
  24. #include "BigEasyGrafish.h"
  25. #include "BigEasyDialogs.h"
  26. #include "BigEasyUtils.h"
  27.  
  28. #define globals
  29. #include "Qwertytunes.h"
  30. #include "Qwertytunes Filing.h"
  31.  
  32. /*--------------------------
  33.     Limits and Konstants
  34. --------------------------*/
  35.  
  36.  
  37. enum
  38.     {
  39.     mNew = 100,
  40.     mOpen,
  41.     mSave,
  42.  
  43.     mFirstDocActive,
  44.  
  45.     mClose,
  46.     mSaveAs,
  47.     mBackgroundPlay,
  48.     mSelectInstrument,
  49.     mSelectChannel,
  50.     mMIDIMessage,
  51.  
  52.     mPortList,
  53.     mLastPortListItem = mPortList + kMaxPortCount,
  54.  
  55.     mQwertytunes,
  56.     mLast
  57.     };
  58.  
  59.  
  60. /*--------------------------
  61.     Types and globals
  62. --------------------------*/
  63.  
  64. #define kDocMargin 10
  65. #define kTextAllowance 16
  66.  
  67. #define kKeyWidth 23
  68. #define kKeyHeight 21
  69. #define kKeyMargin 6
  70. #define kKeyRowBump 7
  71. #define kKeyInfoWidth 120
  72. #define kKeyInfoHeight (4 * kTextAllowance + kDocMargin)
  73. #define kKeyInfoHDivider 60
  74.  
  75. #define kSliderThickness kDocMargin
  76.  
  77. #define kCursorBlinkTicks 14
  78.  
  79. #define SignIt(x) ( (x)?1:-1)
  80.  
  81. /*--------------------------
  82.     Prototypes
  83. --------------------------*/
  84. static void DrawDoc(short n);
  85. static void ClickDoc(short n,Point p,short mods);
  86. static void KeyDoc(short n,short key,short code, short mods);
  87. static void MoveDoc(short n);
  88. static void ZoomDoc(short n);
  89. static short CloseDoc(short n);
  90. static void IdleDoc(short n,Boolean front);
  91. static void BackgroundPlayDoc(short n,short item, short ref);
  92. static void SelectInstrumentDoc(short n,short item, short ref);
  93. static void SelectChannelDoc(short n,short item, short ref);
  94. static void MIDIMessageDoc(short n,short item, short ref);
  95. static void LetsQuit(void);
  96. static void OpenAWindow(void);
  97. static void NewDoc(void);
  98. static void MakeWindow(void);
  99. static void About(void);
  100. static void InitVars(void);
  101. static void SelectKeyDoc(TDoc *d,short key);
  102. static void Draw1Key(TDoc *d,short n);
  103. static void DrawKeyInfoDoc(TDoc *d);
  104. static void DrawKeyNumbersDoc(TDoc *d);
  105. static void FrameNumbersField(Rect *r);
  106. static void SelectFieldDoc(TDoc *d,short f);
  107. static void BlinkTextCursorDoc(TDoc *d,short what);
  108. static void MIDIConnectDoc(short n,short item, short ref);
  109. static void DrawNoteName(short noteNumber);
  110.  
  111. static void FixUpPortListMenu(TDoc *d);
  112. static void TrackMIDIController(short n,Point p,short controller,TDoc *d);
  113.  
  114. static pascal void PeriodicThing(long currentTime, long refCon);
  115. static void SquelchAllKeys(TDoc *d);
  116.  
  117. /*--------------------------
  118.     Computer Programs
  119. --------------------------*/
  120.  
  121. char dKeyPositionToKeyCode[kKeyCount] =
  122.     {
  123.     18,19,20,21,23,22,26,28,25,29,        /* 1234567890 */
  124.     12,13,14,15,17,16,32,34,31,35,        /* qwertyuiop */
  125.      0, 1, 2, 3, 5, 4,38,40,37,41,        /* asdfghjkl; */
  126.      6, 7, 8, 9,11,45,46,43,47,44        /* zxcvbnm,./ */
  127.     };
  128.  
  129. char dKeyPositionToKeyName[kKeyCount] = "1234567890QWERTYUIOPASDFGHJKL;ZXCVBNM,./";
  130.  
  131.  
  132. static short GetModKeys(void);
  133. short GetModKeys(void)
  134. /*
  135.  * return the modifier keys just like in an eventrecord
  136.  */
  137.     {
  138.     short result;
  139.     KeyMap keys;
  140.  
  141.     GetKeys(keys);
  142.  
  143.     result = 0;
  144.     if(keys[1] & 1)
  145.         result |= shiftKey;
  146.     if(keys[1] & 4)
  147.         result |= optionKey;
  148.     if(keys[1] & 8)
  149.         result |= controlKey;
  150.     if(keys[1] & 0x8000)
  151.         result |= cmdKey;
  152.  
  153.     return result;
  154.     }
  155.  
  156.  
  157.  
  158. void DrawDoc(short n)
  159. /*
  160.  * Draws the window.
  161.  */
  162.     {
  163.     Rect *r,re;
  164.     short i;
  165.     TDoc *d;
  166.     char name;
  167.  
  168.     d = &gDoc[n-kFirstDocWindow];
  169.  
  170.     RGBBack(40000,63000,40000);
  171.     EraseRect(&gBigRect);
  172.  
  173.     GoBW();
  174.     TextSize(9);
  175.     TextFont(3);
  176.     for(i = 0; i < kKeyCount; i++)
  177.         Draw1Key(d,i);
  178.  
  179.     for(i = 0; i < kControllerCount; i++)
  180.         {
  181.         re = g->controllerRect[i];
  182.         FrameRect(&re);
  183.         InsetRect(&re,1,1);
  184.         EraseRect(&re);
  185.         }
  186.  
  187.     if(d->sr.zoomed)
  188.         {
  189.         short y;
  190.  
  191.         GoBW();
  192.         PenPat(qd.gray);
  193.         y = g->controllerRect[kControllerCount - 1].bottom + kDocMargin;
  194.         MoveTo(qd.thePort->portRect.left + kDocMargin,y);
  195.         LineTo(qd.thePort->portRect.right - kDocMargin,y);
  196.         MoveTo(g->keyInfoRect.right + kDocMargin,y);
  197.         LineTo(g->keyInfoRect.right + kDocMargin,qd.thePort->portRect.bottom - kDocMargin);
  198.         
  199.         GoBW();
  200.  
  201.         DrawKeyInfoDoc(d);
  202.  
  203.         if(d->tabField == eKeyField)
  204.             {
  205.             re = g->allKeyRect;
  206.             InsetRect(&re,-4,-4);
  207.             FrameRect(&re);
  208.             }
  209.         }
  210.     }
  211.  
  212. void Draw1Key(TDoc *d,short n)
  213.     {
  214.     Rect *r;
  215.     char name;
  216.     unsigned char c[30];
  217.  
  218.     if(n < 0)
  219.         goto goHome;
  220.  
  221.     if(n == d->selectedKey && d->sr.zoomed)
  222.         PenSize(2,2);
  223.     else
  224.         PenSize(1,1);
  225.     r = &g->keyRect[n];
  226.     BackColor(whiteColor);
  227.     EraseRect(r);
  228.     GoBlack();
  229.     FrameRectOutside(r);
  230.     name = dKeyPositionToKeyName[n];
  231.     TextFace(bold);
  232.     MoveTo((r->left + r->right - CharWidth(name))>>1,r->top + kDocMargin);
  233.     DrawChar(name);
  234.  
  235.     TextFace(0);
  236.     NumToString(d->sr.pitch[n],c);
  237.     MoveTo((r->left + r->right - StringWidth(c))>>1,r->bottom - 2);
  238.     DrawString(c);
  239.  
  240.     if(d->keyPosition[n])
  241.         InvertRect(r);
  242. goHome:;
  243.     }
  244.  
  245.  
  246. void SelectKeyDoc(TDoc *d,short key)
  247.     {
  248.     Rect r;
  249.  
  250.     if(!d->sr.zoomed && key >= 0)
  251.         goto goHome;
  252.  
  253.     if(key != d->selectedKey)
  254.         {
  255.         GoBW();
  256.         if(d->selectedKey >= 0)
  257.             {
  258.             r = g->keyRect[d->selectedKey];
  259.             InsetRect(&r,-2,-2);
  260.             RGBFore(40000,63000,40000);
  261.             RGBBack(40000,63000,40000);
  262.             FrameRect(&r);
  263.     
  264.             GoBW();
  265.             }
  266.  
  267.         if(key >= 0)
  268.             {
  269.             PenSize(2,2);
  270.             FrameRectOutside(&g->keyRect[key]);
  271.             }
  272.  
  273.         g->lastKeyDown = d->selectedKey = key;
  274.         DrawKeyNumbersDoc(d);
  275.         }
  276. goHome:;
  277.     }
  278.  
  279. void SelectFieldDoc(TDoc *d,short f)
  280.     {
  281.     Rect re;
  282.  
  283.     if(!d->sr.zoomed && f)
  284.         goto goHome;
  285.  
  286.     re = g->allKeyRect;
  287.     InsetRect(&re,-4,-4);
  288.  
  289.     if(d->tabField != f)
  290.         {
  291.         BlinkTextCursorDoc(d,-1);
  292.         if(d->tabField == eKeyField)
  293.             {
  294.             PenNormal();
  295.             RGBFore(40000,63000,40000);
  296.             RGBBack(40000,63000,40000);
  297.             FrameRect(&re);
  298.             }
  299.  
  300.         d->tabField = f;
  301.     
  302.         if(d->tabField == eKeyField)
  303.             {
  304.             PenNormal();
  305.             FrameRect(&re);
  306.             }
  307.  
  308.         DrawKeyNumbersDoc(d);
  309.         BlinkTextCursorDoc(d,1);
  310.         }
  311. goHome:;
  312.     }
  313.  
  314.  
  315. void DrawKeyInfoDoc(TDoc *d)
  316.     {
  317.     short key;
  318.  
  319.     key = d->selectedKey;
  320.  
  321.     GoBW();
  322.     RGBBack(40000,63000,40000);
  323.     EraseRect(&g->keyInfoRect);
  324.  
  325.     MoveTo(g->keyInfoRect.left,g->keyInfoRect.top + kTextAllowance);
  326.     TextFont(0);
  327.     TextSize(12);
  328.     TextFace(0);
  329.     DrawString("\pKeyboard Setup");
  330.  
  331.     TextFont(3);
  332.     TextSize(9);
  333.     MoveTo(kKeyInfoHDivider,g->keyInfoRect.top + 2*kTextAllowance);
  334.     DrawStringRight("\pKey= ");
  335.  
  336.     MoveTo(kKeyInfoHDivider,g->pitchInfoRect.bottom - 4);
  337.     DrawStringRight("\pPitch: ");
  338.  
  339.     MoveTo(kKeyInfoHDivider,g->velInfoRect.bottom - 4);
  340.     DrawStringRight("\pVelocity: ");
  341.  
  342.     DrawKeyNumbersDoc(d);
  343.     }
  344.  
  345.  
  346. void FrameNumbersField(Rect *r)
  347.     {
  348.     Rect re;
  349.  
  350.     re = *r;
  351.     FrameRect(&re);
  352.     InsetRect(&re,+1,+1);
  353.     EraseRect(&re);
  354.     }
  355.  
  356. void DrawKeyNumbersDoc(TDoc *d)
  357.     {
  358.     short x;
  359.     short key;
  360.     Rect r;
  361.  
  362.     key = d->selectedKey;
  363.  
  364.     if(key >= 0)
  365.         Draw1Key(d,key);
  366.  
  367.     GoBW();
  368.     RGBBack(40000,63000,40000);
  369.     r = g->keyInfoRect;
  370.     r.top += kTextAllowance + kDocMargin - 4;
  371.     r.left = kKeyInfoHDivider;
  372.     EraseRect(&r);
  373.     GoBW();
  374.  
  375.     x = kKeyInfoHDivider + 5;
  376.  
  377.     TextSize(9);
  378.     TextFace(3);
  379.     TextFace(0);
  380.  
  381.     MoveTo(x,g->keyInfoRect.top + 2*kTextAllowance);
  382.     DrawChar('\'');
  383.     TextFace(bold);
  384.     DrawChar(dKeyPositionToKeyName[key]);
  385.     TextFace(0);
  386.     DrawChar('\'');
  387.     TextFace(bold);
  388.  
  389.     d->textCursorPt.h = -100;
  390.  
  391.     MoveTo(x,g->pitchInfoRect.bottom - 4);
  392.     if(d->tabField == ePitchField)
  393.         {
  394.         FrameNumbersField(&g->pitchInfoRect);
  395.         DrawNoteName(d->sr.pitch[key]);
  396.         GetPen(&d->textCursorPt);
  397.         }
  398.     else
  399.         DrawNoteName(d->sr.pitch[key]);
  400.  
  401.     MoveTo(x,g->velInfoRect.bottom - 4);
  402.     if(d->tabField == eVelField)
  403.         {
  404.         FrameNumbersField(&g->velInfoRect);
  405.         DrawNum(d->sr.vel[key]);
  406.         GetPen(&d->textCursorPt);
  407.         }
  408.     else
  409.         DrawNum(d->sr.vel[key]);
  410.     }
  411.  
  412.  
  413.  
  414.  
  415. void ClickDoc(short n,Point p,short mods)
  416. /*
  417.  * Come here for a click in the window.
  418.  */
  419.     {
  420.     #pragma unused (mods)
  421.     Rect r;
  422.     short i;
  423.     TDoc *d;
  424.  
  425.     d = &gDoc[n-kFirstDocWindow];
  426.  
  427.     for(i = 0; i<kControllerCount; i++)
  428.         {
  429.         if(PtInRect(p,&g->controllerRect[i]))
  430.             {
  431.             TrackMIDIController(n,p,i,d);
  432.             goto goHome;
  433.             }
  434.         }
  435.  
  436.     if(PtInRect(p,&g->allKeyRect))
  437.         {
  438.         for(i = 0; i<kKeyCount; i++)
  439.             {
  440.             if(PtInRect(p,&g->keyRect[i]))
  441.                 {
  442.                 SelectKeyDoc(d,i);
  443.                 SelectFieldDoc(d,eKeyField);
  444.                 g->lastKeyDown = i;
  445.                 }
  446.             }
  447.         }
  448.  
  449.     else if(PtInRect(p,&g->pitchInfoRect))
  450.         SelectFieldDoc(d,ePitchField);
  451.  
  452.     else if(PtInRect(p,&g->velInfoRect))
  453.         SelectFieldDoc(d,eVelField);
  454.  
  455. goHome:;
  456.     }
  457.  
  458. void TrackMIDIController(short n,Point p,short controller,TDoc *d)
  459.     {
  460.     Rect r,slopR;
  461.     long x,lastX;
  462.     MIDIPacket b;
  463.  
  464.     r = g->controllerRect[controller];
  465.  
  466.     if(controller == 0)
  467.         {
  468.         b.data[0] = 0xE0 + d->sr.channel;
  469.         b.data[1] = 0;
  470.         }
  471.     else
  472.         {
  473.         b.data[0] = 0xB0 + d->sr.channel;
  474.         if(controller == 1)
  475.             b.data[1] = 1;
  476.         else
  477.             b.data[1] = 7;
  478.         }
  479.  
  480.     slopR = r;
  481.     InsetRect(&slopR,-10,-10);
  482.  
  483.     b.len = 9;
  484.     b.flags = 0x80;
  485.  
  486.     lastX = -1;
  487.     do
  488.         {
  489.         IdleDoc(n,true);
  490.         x = 127L * (p.h - r.left) / (r.right - r.left);
  491.         if(x < 0)
  492.             x = 0;
  493.         else if(x > 127)
  494.             x = 127;
  495.  
  496.         if(x != lastX)
  497.             {
  498.             b.data[2] = x;
  499.             MIDIWritePacket(d->outPort,&b);
  500.             lastX = x;
  501.             }
  502.  
  503.         GetMouse(&p);
  504.         } while(StillDown());
  505.  
  506.     }
  507.  
  508.  
  509. static unsigned char *dNoteName[] =
  510.     {
  511.     "\pC",
  512.     "\pC#",
  513.     "\pD",
  514.     "\pD#",
  515.     "\pE",
  516.     "\pF",
  517.     "\pF#",
  518.     "\pG",
  519.     "\pG#",
  520.     "\pA",
  521.     "\pA#",
  522.     "\pB"
  523.     };
  524.  
  525. void DrawNoteName(short noteNumber)
  526. /*
  527.  * Print the MIDI note number as a string
  528.  */
  529.     {
  530.     short o,n;
  531.  
  532.     if(noteNumber < 0)
  533.         noteNumber = 0;
  534.     else if(noteNumber > 127)
  535.         noteNumber = 127;
  536.  
  537.     o = noteNumber/12;
  538.     n = noteNumber % 12;
  539.  
  540.     DrawString(dNoteName[n]);
  541.     DrawNum(o-2);
  542.     DrawString("\p (");
  543.     DrawNum(noteNumber);
  544.     DrawChar(')');
  545.     }
  546.  
  547.  
  548.  
  549.  
  550. void KeyDoc(short n,short key,short code, short mods)
  551.     {
  552.     short *val;
  553.     short z;
  554.     TDoc *d;
  555.     short lastPitch,lastVel;
  556.  
  557.     d = &gDoc[n-kFirstDocWindow];
  558.  
  559.     if(!d->sr.zoomed)
  560.         goto goHome;
  561.  
  562.     lastPitch = d->sr.pitch[d->selectedKey];
  563.     lastVel = d->sr.vel[d->selectedKey];
  564.  
  565.     if(d->tabField != eVelField)
  566.         val = d->sr.pitch;
  567.     else
  568.         val = d->sr.vel;
  569.  
  570.     val += d->selectedKey;
  571.  
  572.     if(key >= '0' && key <= '9' && (d->tabField != eKeyField || code >= 65))
  573.         {
  574.         *val = 10 * (*val % 10) + (key - '0');
  575.         DrawKeyNumbersDoc(d);        
  576.         d->changed = true;
  577.         }
  578.     else
  579.         switch(key)
  580.             {
  581.             case '+':
  582.             case '=':
  583.                 z = 1;
  584.         bumpVal:
  585.                 if(mods & shiftKey)
  586.                     z *= 10;
  587.                 *val = (z + *val) & 0x7F;
  588.                 DrawKeyNumbersDoc(d);
  589.                 d->changed = true;
  590.                 break;
  591.  
  592.             case '-':
  593.             case '_':
  594.                 z = -1;
  595.                 goto bumpVal;
  596.  
  597.             case 9:    /* tab */
  598.                 if(mods & shiftKey)
  599.                     {
  600.                     z = d->tabField - 1;
  601.                     if(z <= eFirstField)
  602.                         z = eLastField - 1;
  603.                     }
  604.                 else
  605.                     {
  606.                     z = d->tabField + 1;
  607.                     if(z >= eLastField)
  608.                         z = eFirstField + 1;
  609.                     }
  610.                 SelectFieldDoc(d,z);
  611.                 break;
  612.  
  613.             case 28:        /* left arrow */
  614.                 z = (d->selectedKey + kKeyCount-1) % kKeyCount;
  615.             arrowMove:
  616.                 if(mods & optionKey)
  617.                     {
  618.                     d->sr.pitch[z] = (lastPitch + 1) & 0x7F;
  619.                     d->sr.vel[z] = lastVel;
  620.                     d->changed = true;
  621.                     }
  622.                 SelectKeyDoc(d,z);
  623.                 break;
  624.  
  625.             case 29:        /* right arrow */
  626.             doRightArrow:
  627.                 z = (d->selectedKey + 1) % kKeyCount;
  628.                 goto arrowMove;
  629.                 break;
  630.  
  631.             case 30:        /* up arrow */
  632.                 z = d->selectedKey - kKeyColumns;
  633.                 if(z == -kKeyColumns)
  634.                     z = kKeyCount-1;
  635.                 if(z < 0)
  636.                     z += kKeyCount-1;
  637.                 goto arrowMove;
  638.  
  639.             case 31:        /* down arrow */
  640.                 z = d->selectedKey + kKeyColumns;
  641.                 if(z == kKeyCount+kKeyColumns-1)
  642.                     z = 0;
  643.                 if (z >= kKeyCount)
  644.                     z -= kKeyCount-1;
  645.                 goto arrowMove;
  646.             }
  647.  
  648.     FixUpMenus(d);
  649. goHome:;
  650.     }
  651.  
  652. void MoveDoc(short n)
  653.     {
  654.     TDoc *d;
  655.  
  656.     d = &gDoc[n-kFirstDocWindow];
  657.  
  658.     d->littleChanged = true;
  659.     FixUpMenus(d);
  660.     }
  661.  
  662. void ZoomDoc(short n)
  663.     {
  664.     short w,h;
  665.     TDoc *d;
  666.  
  667.     d = &gDoc[n-kFirstDocWindow];
  668.  
  669.     d->sr.zoomed = !d->sr.zoomed;
  670.     if(d->sr.zoomed)
  671.         {
  672.         w = g->zoomedBounds.right;
  673.         h = g->zoomedBounds.bottom;
  674.         SelectKeyDoc(d,g->lastKeyDown >= 0 ? g->lastKeyDown : 0);
  675.         SelectFieldDoc(d,eKeyField);
  676.         }
  677.     else
  678.         {
  679.         w = g->unzoomedBounds.right;
  680.         h = g->unzoomedBounds.bottom;
  681.         SelectFieldDoc(d,0);
  682.         SelectKeyDoc(d,-1);
  683.         SelectFieldDoc(d,0);
  684.         }
  685.  
  686.     SizeWindow(d->w,w,h,true);
  687.  
  688.     d->changed = true;
  689.     FixUpMenus(d);
  690.     }
  691.  
  692. short CloseDoc(short n)
  693. /*
  694.  * Close that window...
  695.  * Return 'true' if the
  696.  * user canceled the save. This is useful
  697.  * when 'quitting' but then cancelling.
  698.  */
  699.     {
  700.     short x;
  701.     TDoc *d;
  702.     Boolean cancelSave;
  703.  
  704.     d = &gDoc[n - kFirstDocWindow];
  705.  
  706.     DeactivateDoc(n);
  707.     SquelchAllKeys(d);
  708.  
  709.     if(d->used && d->changed)
  710.         {
  711.         x = EasyDialogMessage(0,d->docSpec.name,"\pSave changes before closing this document?",
  712.                 kEasyDialogSaveDiscardCancel);
  713.         if(x == 2)
  714.             cancelSave = true;
  715.         else if(x == 0)
  716.             cancelSave = SaveDoc(n,0,0);
  717.         else
  718.             cancelSave = false;
  719.         }
  720.     else
  721.         cancelSave = false;
  722.  
  723.     if(!cancelSave)
  724.         {
  725.         UninstallWindow(n);
  726.         g->thisDoc = 0;
  727.         MIDIRemovePort(d->outPort);
  728.         d->used = false;
  729.         gDocCount--;
  730.         }
  731.  
  732.     FixUpMenus(0);
  733.  
  734.     return cancelSave;
  735.     }
  736.  
  737. void FixUpMenus(TDoc *d)
  738.     /*
  739.      * Make menus suitable for document d,
  740.      * where d=nil means deactivate
  741.      */
  742.     {
  743.     short moreDocs;
  744.  
  745.     moreDocs = SignIt(gDocCount<kDocMax);
  746.  
  747.     SetMenuItem(mNew,moreDocs,0,0,nil);
  748.     SetMenuItem(mOpen,moreDocs,0,0,nil);
  749.  
  750.     FixUpPortListMenu(d);
  751.  
  752.     if(d)
  753.         {
  754.          SetMenuItem(mSave,SignIt(d->changed || d->littleChanged),0,0,nil);
  755.          SetMenuItem(mClose,1,0,0,nil);        /* enable "Close" menu item        */
  756.         SetMenuItemRange(mFirstDocActive,mLast,1,-1);
  757.         SetMenuItem(mBackgroundPlay,1,SignIt(d->sr.backgroundPlay),18,nil);
  758.         }
  759.     else
  760.         {
  761.          SetMenuItem(mSave,-1,0,0,nil);
  762.         SetMenuItemRange(mFirstDocActive,mLast,-1,-1);
  763.         }
  764.     }
  765.  
  766. void FixUpPortListMenu(TDoc *d)
  767. /*
  768.  * Yeah, we do this every time that the menus
  769.  * might change, bunch of gethandle this
  770.  * and that. Okay? Keeps the menus looking good.
  771.  */
  772.     {
  773.     short i,j,k;
  774.     short portCount;
  775.     MIDIIDList **clients,**ports;
  776.     OSType portID,clientID;
  777.     MIDIPortInfo **portInfo;
  778.     short usConnected;
  779.     Str255 s255;
  780.     StringPtr sp;
  781.  
  782.     SetCurrentMenu(mQwertytunes);
  783.     for(i = mPortList; i < mLastPortListItem; i++)
  784.         DeleteMenuItem(i);
  785.  
  786.     portCount = 0;
  787.     clients = MIDIGetClients();
  788.     for(i = 0; i < (**clients).numIDs; i++)
  789.         {
  790.         clientID = (**clients).list[i];
  791.         ports = MIDIGetPorts( clientID );
  792.         for(j = 0; j < (**ports).numIDs; j++)
  793.             {
  794.             portID = (**ports).list[j];
  795.             portInfo = MIDIGetPortInfo(clientID,portID);
  796.             if( (**portInfo).portType == midiPortTypeInput)
  797.                 {
  798.                 usConnected = -1;
  799.                 if(d)
  800.                     for(k = 0; k < (**portInfo).numConnects; k++)
  801.                         {
  802.                         if( (**portInfo).cList[k].clientID == g->midiClientID
  803.                                 && (**portInfo).cList[k].portID == d->outPortID)
  804.                             usConnected = 1;
  805.                         }
  806.                 g->clientList[portCount] = clientID;
  807.                 g->portList[portCount] = portID;
  808.                 sp = g->portName[portCount];
  809.                 MIDIGetClientName(clientID,sp);
  810.                 MIDIGetPortName(clientID,portID,s255);
  811.                 ConcatenatePStrings(sp,"\p, ");
  812.                 ConcatenatePStrings(sp,s255);
  813.                 InstallMenuItem(g->portName[portCount],MIDIConnectDoc,
  814.                         (mPortList + portCount) * SignIt(d != 0));
  815.                 SetMenuItem(mPortList + portCount,SignIt(d!=0),usConnected,18,nil);
  816.                 portCount ++;
  817.                 if(portCount == kMaxPortCount)
  818.                     {
  819.                     DisposeHandle((Handle)portInfo);
  820.                     DisposeHandle((Handle)ports);
  821.                     DisposeHandle((Handle)clients);
  822.                     goto goHome;
  823.                     }
  824.                 }
  825.             DisposeHandle((Handle)portInfo);
  826.             }
  827.         DisposeHandle((Handle)ports);
  828.         }
  829.     DisposeHandle((Handle)clients);
  830.         
  831. goHome:;
  832.     }
  833.  
  834.  
  835. void MIDIConnectDoc(short n,short item, short ref)
  836.     {
  837.     TDoc *d;
  838.     MIDIPortInfo **portInfo;
  839.     short i,j;
  840.     OSType clientID,portID;
  841.  
  842.     d = &gDoc[n-kFirstDocWindow];
  843.  
  844.     ref -= mPortList;
  845.     clientID = g->clientList[ref];
  846.     portID = g->portList[ref];
  847.     portInfo = MIDIGetPortInfo(g->midiClientID,d->outPortID);
  848.  
  849.     /*
  850.      * See if we're already connected
  851.      */
  852.     for(i = 0; i < (**portInfo).numConnects; i++)
  853.         {
  854.         if( (**portInfo).cList[i].clientID == clientID
  855.                 && (**portInfo).cList[i].portID == portID)
  856.             {
  857.             MIDIUnConnectData(g->midiClientID,d->outPortID,clientID,portID);
  858.             goto x;
  859.             }
  860.         }
  861.  
  862.     MIDIConnectData(g->midiClientID,d->outPortID,clientID,portID);
  863. x:
  864.     DisposeHandle((Handle)portInfo);
  865.     d->changed = true;
  866.     FixUpMenus(d);
  867.     }
  868.  
  869.  
  870. void FixUpDocConnectionList(TDoc *d)
  871. /*
  872.  * Just before saving a doc,
  873.  * see who we're connected to
  874.  * and remember these things.
  875.  */
  876.     {
  877.     MIDIPortInfo **portInfo;
  878.     short portCount,i;
  879.  
  880.     portInfo = MIDIGetPortInfo(g->midiClientID,d->outPortID);
  881.     portCount = (**portInfo).numConnects;
  882.     if(portCount > kMaxPortCount)
  883.         portCount = kMaxPortCount;
  884.  
  885.     for(i = 0; i < portCount; i++)
  886.         {
  887.         d->sr.portList[i] = (**portInfo).cList[i].portID;
  888.         d->sr.clientList[i] = (**portInfo).cList[i].clientID;
  889.         }
  890.     d->sr.portCount = portCount;
  891.     DisposeHandle((Handle)portInfo);
  892.     }
  893.  
  894.  
  895. void ActivateDoc(short n)
  896.     {
  897.     TDoc *d;
  898.  
  899.     d = &gDoc[n-kFirstDocWindow];
  900.  
  901.     FixUpMenus(d);
  902.  
  903.     g->thisDoc = d;
  904.     if(d->selectedKey >= 0)
  905.         g->lastKeyDown = d->selectedKey;
  906.     BlinkTextCursorDoc(d,1);
  907.     }
  908.  
  909. void DeactivateDoc(short n)
  910.     {
  911.     TDoc *d;
  912.  
  913.     d = &gDoc[n-kFirstDocWindow];
  914.     if(!d->sr.backgroundPlay)
  915.         {
  916.         SquelchAllKeys(d);
  917.         g->thisDoc = 0;
  918.         }
  919.  
  920.  
  921.     FixUpMenus(nil);
  922.  
  923.     BlinkTextCursorDoc(d,-1);
  924.     }
  925.  
  926. void BlinkTextCursorDoc(TDoc *d,short what)
  927. /*
  928.  * what = 1 to start blinking, 0 to blink, and -1 to remove it.
  929.  */
  930.     {
  931.     long t;
  932.  
  933.     GoBW();
  934.     PenMode(patXor);
  935.     MoveTo(d->textCursorPt.h,d->textCursorPt.v);
  936.  
  937.     t = TickCount();
  938.  
  939.     switch(what)
  940.         {
  941.         case 1:
  942.             d->textCursorBlink = true;
  943.         doBlink:
  944.             d->textCursorTicks = t + kCursorBlinkTicks;
  945.             Line(0,-9);
  946.             d->textCursorPhase = !d->textCursorPhase;
  947.             break;
  948.  
  949.         case 0:
  950.             if(d->textCursorBlink && t >= d->textCursorTicks)
  951.                 goto doBlink;
  952.             break;
  953.  
  954.         case -1:
  955.             d->textCursorBlink = false;
  956.             if(d->textCursorPhase)
  957.                 goto doBlink;
  958.         }
  959.     GoBW();
  960.             
  961.     }
  962.  
  963.  
  964. void IdleDoc(short n,Boolean front)
  965.     {
  966.     short i;
  967.     TDoc *d;
  968.     Boolean snik;
  969.  
  970.     d = &gDoc[n-kFirstDocWindow];
  971.  
  972.     if(front)
  973.         {
  974.         BlinkTextCursorDoc(d,0);
  975.  
  976.         if(d->tabField == eKeyField)
  977.             if((g->lastKeyDown >= 0) && (d->selectedKey != g->lastKeyDown))
  978.                 SelectKeyDoc(d,g->lastKeyDown);
  979.     
  980.         for(i = 0; i < kKeyCount; i++)
  981.             {
  982.             snik = g->keyPosition[i];            /* since it _could_ change even now... */
  983.             if(snik != d->keyPosition[i])
  984.                 {
  985.                 InvertRect(&g->keyRect[i]);
  986.                 d->keyPosition[i] = snik;
  987.                 }
  988.             }
  989.         }
  990.  
  991.     }
  992.  
  993.  
  994. void BackgroundPlayDoc(short n,short item, short ref)
  995.     {
  996.     TDoc *d;
  997.  
  998.     d = &gDoc[n-kFirstDocWindow];
  999.  
  1000.     d->sr.backgroundPlay = !d->sr.backgroundPlay;
  1001.     d->changed = true;
  1002.     FixUpMenus(d);
  1003.     }
  1004.  
  1005. void SelectInstrumentDoc(short n,short item, short ref)
  1006.     {
  1007.     Boolean took;
  1008.     TDoc *d;
  1009.     MIDIPacket packet;
  1010.  
  1011.     d = &gDoc[n-kFirstDocWindow];
  1012.     SquelchAllKeys(d);
  1013.  
  1014.     do
  1015.         took = EasyDialogGetNumber("\pSelect Instrument",
  1016.             "\pSelect a new instrument number, 1 - 128:",
  1017.             &d->instrument);
  1018.         while(took && (d->instrument < 1 || d->instrument > 16384));
  1019.  
  1020.     if(took)
  1021.         {
  1022.         packet.flags = 0x80;
  1023.  
  1024.         packet.len = 9;
  1025.         packet.data[0] = 0xB0 + d->sr.channel;
  1026.         packet.data[1] = 0;
  1027.         packet.data[2] = (d->instrument-1) >> 7;
  1028.         MIDIWritePacket(d->outPort,&packet);
  1029.  
  1030.         packet.len = 9;
  1031.         packet.data[0] = 0xB0 + d->sr.channel;
  1032.         packet.data[1] = 32;
  1033.         packet.data[2] = 0;
  1034.         MIDIWritePacket(d->outPort,&packet);            // send bank lsb of 0
  1035.  
  1036.         packet.len = 8;
  1037.         packet.data[0] = 0xC0 + d->sr.channel;
  1038.         packet.data[1] = (d->instrument-1) & 0x7F;
  1039.         MIDIWritePacket(d->outPort,&packet);
  1040.         }
  1041.     }
  1042.  
  1043. void SelectChannelDoc(short n,short item, short ref)
  1044.     {
  1045.     Boolean took;
  1046.     TDoc *d;
  1047.     long newChan;
  1048.  
  1049.     d = &gDoc[n-kFirstDocWindow];
  1050.     SquelchAllKeys(d);
  1051.  
  1052.     newChan = d->sr.channel + 1;
  1053.     d->sr.channel++;
  1054.  
  1055.     do
  1056.         took = EasyDialogGetNumber("\pSelect MIDI Channel",
  1057.             "\pSelect a new MIDI channel, 1 - 16:",
  1058.             &newChan);
  1059.         while(took && (d->sr.channel < 1 || d->sr.channel > 16));
  1060.  
  1061.     d->sr.channel = newChan - 1;
  1062.     }
  1063.  
  1064. static Str255 gMS = "\p";
  1065.  
  1066. static void MIDIMessageDoc(short n,short item, short ref)
  1067.     {
  1068.     Boolean took;
  1069.     TDoc *d;
  1070.     MIDIPacket packet;
  1071.     short hLen,sLen;
  1072.     unsigned char *w;
  1073.     short phase;
  1074.     short d1,d2;
  1075.  
  1076.  
  1077.     d = &gDoc[n-kFirstDocWindow];
  1078.     SquelchAllKeys(d);
  1079.  
  1080.     took = EasyDialogGetString("\pSend MIDI Message",
  1081.             "\pEnter message in hexadecimal:",
  1082.             gMS,255);
  1083.  
  1084.     if(took)
  1085.         {
  1086.         hLen = 0;
  1087.         phase = 0;
  1088.         sLen = gMS[0];
  1089.         w = &gMS[1];
  1090.  
  1091.         while(sLen)
  1092.             {
  1093.             if(*w >= 'a' && *w <= 'f')
  1094.                 d1 = *w - 'a' + 10;
  1095.             else if(*w >= 'A' && *w <= 'F')
  1096.                 d1 = *w - 'A' + 10;
  1097.             else if(*w >= '0' && *w <= '9')
  1098.                 d1 = *w - '0';
  1099.             else
  1100.                 goto blankchar;
  1101.  
  1102.             if(phase)
  1103.                 {
  1104.                 phase = 0;
  1105.                 packet.data[hLen] = d2+d1;
  1106.                 hLen++;
  1107.                 }
  1108.             else
  1109.                 {
  1110.                 phase = 1;
  1111.                 d2 = d1<<4;
  1112.                 }
  1113.     blankchar:
  1114.             w++;
  1115.             sLen--;
  1116.             }
  1117.  
  1118.         packet.flags = 0x80;
  1119.         packet.len = 6 + hLen;
  1120.         packet.tStamp = 0;
  1121.         MIDIWritePacket(d->outPort,&packet);
  1122.         }
  1123.  
  1124.     }
  1125.  
  1126.  
  1127. pascal void PeriodicThing(long currentTime, long refCon)
  1128.     {
  1129.     TGlobals *g;
  1130.     char diffMask[16];
  1131.     char *newMask;
  1132.     char x;
  1133.     char mask;
  1134.     short i,bit,key;
  1135.     Boolean cmdKeyDown;
  1136.     MIDIPacket packet;
  1137.     TDoc *d;
  1138.  
  1139.     g = (void *)refCon;
  1140.     d = g->thisDoc;
  1141.  
  1142.     newMask = (char *)0x174;    /* Low-mem keyboard mask */
  1143.  
  1144.     for(i = 15; i>= 0; i--)
  1145.         diffMask[i] = g->keyMask[i] ^ newMask[i];
  1146.  
  1147.     cmdKeyDown = (newMask[6] & 0x80) != 0;
  1148.  
  1149.  
  1150.     for(i = 15; i>=0; i--)
  1151.         {
  1152.         x = diffMask[i];
  1153.         mask = 0x01;
  1154.         bit = 0;
  1155.         while(x && mask)
  1156.             {
  1157.             if(x & mask)
  1158.                 {
  1159.                 key = g->keyCodeToKeyPosition[(i<<3) + bit];
  1160.                 if(key >= 0)
  1161.                     {
  1162.                     if(d)
  1163.                         {
  1164.                         packet.flags = 0x80;
  1165.                         packet.len = 9;
  1166.                         packet.data[0] = 0x90 + d->sr.channel;
  1167.                         packet.data[1] = d->sr.pitch[key];
  1168.                         }
  1169.  
  1170.                     if(newMask[i] & mask && !cmdKeyDown)
  1171.                         {                        /* key-down */
  1172.                         g->keyPosition[key] = true;
  1173.                         g->lastKeyDown = key;
  1174.                         if(d)
  1175.                             packet.data[2] = d->sr.vel[key];
  1176.                         }
  1177.                     else
  1178.                         {                        /* key-up */
  1179.                         g->keyPosition[key] = false;
  1180.                         if(d)
  1181.                             packet.data[2] = 0;
  1182.                         }
  1183.  
  1184.                     if(d)
  1185.                         MIDIWritePacket(d->outPort,&packet);
  1186.                     }
  1187.                 }
  1188.             mask <<= 1;
  1189.             ++ bit;
  1190.             }
  1191.         }
  1192.  
  1193.     for(i = 15; i>= 0; i--)
  1194.         g->keyMask[i] = newMask[i];
  1195.     }
  1196.  
  1197.  
  1198. void SquelchAllKeys(TDoc *d)
  1199.     {
  1200.     MIDIPacket packet;
  1201.     short i;
  1202.  
  1203.     for(i = 0; i < kKeyCount; i++)
  1204.         {
  1205.         if(g->keyPosition[i])
  1206.             {
  1207.             g->keyPosition[i] = false;
  1208.  
  1209.             packet.flags = 0x80;
  1210.             packet.len = 9;
  1211.             packet.data[0] = 0x90 + d->sr.channel;
  1212.             packet.data[1] = d->sr.pitch[i];
  1213.             packet.data[2] = 0;                    /* vel = 0 -> keyoff */
  1214.     
  1215.             MIDIWritePacket(d->outPort,&packet);
  1216.             }
  1217.         }
  1218.     }
  1219.  
  1220.  
  1221.  
  1222. void LetsQuit(void)
  1223.     {
  1224.     short i;
  1225.     TDoc *d;
  1226.     Boolean cancelQuit;
  1227.  
  1228.     cancelQuit = false;
  1229.     for(i = 0; i<kDocMax && !cancelQuit; i++)
  1230.         {
  1231.         d = &gDoc[i];
  1232.  
  1233.         if(d->used)
  1234.             cancelQuit = CloseDoc(i + kFirstDocWindow);
  1235.         }
  1236.  
  1237.     if(!cancelQuit)
  1238.         gQuitApp++;
  1239.     }
  1240.  
  1241.  
  1242. void NewDoc(void)
  1243.     {
  1244.     short i;
  1245.  
  1246.     for(i = 0; i<kDocMax; i++)
  1247.         {
  1248.         if(!gDoc[i].used)
  1249.             {
  1250.             NewDocFromSaveRecord(i,nil);
  1251.             goto goHome;
  1252.             }
  1253.         }
  1254. goHome:;
  1255.     }
  1256.  
  1257.  
  1258. void NewDocFromSaveRecord(short docNumber,TSaveRecord *sr)
  1259.     {
  1260.     TDoc *d;
  1261.     Rect r;
  1262.     Rect docRect;
  1263.     short i,j;
  1264.  
  1265.     d = &gDoc[docNumber];
  1266.  
  1267.     d->changed = false;
  1268.  
  1269.     docRect = g->unzoomedBounds;
  1270.     if(sr)
  1271.         {
  1272.         d->sr = sr->sr;
  1273.         gStaggerWindows = false;
  1274.         if(d->sr.zoomed)
  1275.             docRect = g->zoomedBounds;
  1276.         OffsetRect(&docRect,sr->windowRect.left,sr->windowRect.top);
  1277.  
  1278.         d->everSaved = true;
  1279.         d->littleChanged = false;
  1280.  
  1281.         if(d->sr.zoomed)
  1282.             {
  1283.             if(g->lastKeyDown >= 0)
  1284.                 d->selectedKey = g->lastKeyDown;
  1285.             else
  1286.                 d->selectedKey = g->lastKeyDown = 0;
  1287.             d->tabField = eKeyField;
  1288.             }
  1289.         else
  1290.             {
  1291.             d->selectedKey = -1;
  1292.             d->tabField = 0;
  1293.             }
  1294.  
  1295.         if(d->sr.portCount > kMaxPortCount)
  1296.             d->sr.portCount = 0;
  1297.  
  1298.         }
  1299.     else
  1300.         {
  1301.         gStaggerWindows = true;
  1302.         OffsetRect(&docRect,100,100);
  1303.  
  1304.         CopyPString(d->docSpec.name,"\pUntitled");
  1305.         d->everSaved = false;
  1306.  
  1307.         d->sr.channel = 0;
  1308.         for(i = 0; i < kKeyCount; i++)
  1309.             {
  1310.             d->sr.pitch[i] = i + 48;
  1311.             d->sr.vel[i] = 64;
  1312.             d->sr.dur[i] = 0;            /* key-controlled duration */
  1313.             }
  1314.         d->sr.zoomed = false;
  1315.         d->sr.backgroundPlay = false;
  1316.         d->littleChanged = true;
  1317.         d->selectedKey = -1;
  1318.         d->tabField = 0;
  1319.         d->sr.portCount = 0;
  1320.         }
  1321.  
  1322.     /*
  1323.      * Clear the keyboard mask
  1324.      */
  1325.     for(i = 0; i < kKeyCount; i++)
  1326.         d->keyPosition[i] = 0;
  1327.  
  1328.     d->instrument = 1;
  1329.  
  1330.     d->textCursorPhase = 0;
  1331.     d->textCursorBlink = false;
  1332.     d->textCursorPt.h = -100;
  1333.  
  1334.     /*
  1335.      * MIDI port
  1336.      */
  1337.         {
  1338.         MIDIPortParams mp;
  1339.         OSErr thisError;
  1340.  
  1341.         d->outPortID = 'dvb!';
  1342.     
  1343.         mp.portID = d->outPortID;
  1344.         mp.portType = midiPortTypeOutput;
  1345.         mp.timeBase = g->timePort;
  1346.         mp.offsetTime = 0;
  1347.         mp.readHook = 0;
  1348.         mp.refCon = 0;
  1349.         BlockMove(d->docSpec.name,&mp.name,32);
  1350.         thisError = MIDIAddPort(g->midiClientID,0,&d->outPort,&mp);        /* error?? */
  1351.         }
  1352.  
  1353.     for(i = 0; i < d->sr.portCount; i++)
  1354.         MIDIConnectData(g->midiClientID,d->outPortID,
  1355.                 d->sr.clientList[i],d->sr.portList[i]);
  1356.  
  1357.     d->w = InstallWindow(docNumber + kFirstDocWindow,d->docSpec.name,&docRect,
  1358.             zoomDocProc,wCopyDraw,
  1359.             DrawDoc,ClickDoc,KeyDoc,(void *)CloseDoc,
  1360.             ActivateDoc,DeactivateDoc,IdleDoc);
  1361.     SetWindowMoveProc(docNumber + kFirstDocWindow,MoveDoc);
  1362.     SetWindowZoomProc(docNumber + kFirstDocWindow,ZoomDoc);
  1363.  
  1364.     d->used = true;
  1365.  
  1366.     gDocCount++;
  1367. goHome:;
  1368.     }
  1369.  
  1370. void About(void)
  1371.     {
  1372.     if(g->thisDoc && !g->thisDoc->sr.backgroundPlay)
  1373.         SquelchAllKeys(g->thisDoc);
  1374.     EasyDialogMessage(0,(StringPtr)0x910,
  1375.             "\pVersion 0.6 by David Van Brink",
  1376.             kEasyDialogOkay);
  1377.     }
  1378.  
  1379. void InitVars(void)
  1380. /*
  1381.  * Called once at startup: yes, it
  1382.  * inits the vars.
  1383.  */
  1384.     {
  1385.     TDoc *dp;
  1386.     short i,j;
  1387.     Rect r,ar;
  1388.  
  1389.     for(i = 0; i<kDocMax; i++)
  1390.         {
  1391.         dp = &gDoc[i];
  1392.         dp->used = false;
  1393.         }
  1394.     gDocCount = 0;
  1395.  
  1396.     g = (void *)NewPtr(sizeof(TGlobals));
  1397.  
  1398.     /*
  1399.      * Build up a list of rectangles for
  1400.      * drawing the keyboard.
  1401.      */
  1402.     r.top = r.left = kDocMargin;
  1403.     r.bottom = r.top + kKeyHeight;
  1404.     r.right = r.left + kKeyWidth;
  1405.     for(i = 0; i < kKeyRows; i++)
  1406.         {
  1407.         OffsetRect(&r,kDocMargin + i * kKeyRowBump - r.left,0);
  1408.         for(j = 0; j < kKeyColumns; j++)
  1409.             {
  1410.             g->keyRect[i * kKeyColumns + j] = r;
  1411.             if(i || j)
  1412.                 UnionRect(&ar,&r,&ar);
  1413.             else
  1414.                 ar = r;
  1415.             OffsetRect(&r,kKeyWidth + kKeyMargin,0);
  1416.             }
  1417.         OffsetRect(&r,0,kKeyHeight + kKeyMargin);
  1418.         }
  1419.     g->allKeyRect = ar;
  1420.  
  1421.     for(i = 0; i<kControllerCount; i++)
  1422.         {
  1423.         r.left = g->allKeyRect.left;
  1424.         r.right = g->allKeyRect.right;
  1425.         r.top = g->allKeyRect.bottom + kDocMargin;
  1426.         r.bottom = r.top + kDocMargin;
  1427.         OffsetRect(&r,0,i * kDocMargin * 2);
  1428.         g->controllerRect[i] = r;
  1429.         }
  1430.  
  1431.     UnionRect(&ar,&g->controllerRect[kControllerCount - 1],&ar);
  1432.  
  1433.     /*
  1434.      * Position of pitch and key info.
  1435.      */
  1436.     g->keyInfoRect.left = g->allKeyRect.left;
  1437.     g->keyInfoRect.right = g->keyInfoRect.left + kKeyInfoWidth;
  1438.     g->keyInfoRect.top = g->controllerRect[kControllerCount - 1].bottom + 1 + kDocMargin;
  1439.     g->keyInfoRect.bottom = g->keyInfoRect.top + kKeyInfoHeight;
  1440.  
  1441.     g->pitchInfoRect.left = kKeyInfoHDivider + 2;
  1442.     g->pitchInfoRect.top = g->keyInfoRect.top + 3*kTextAllowance - 11;
  1443.     g->pitchInfoRect.right = g->keyInfoRect.right - 3;
  1444.     g->pitchInfoRect.bottom = g->pitchInfoRect.top + 15;
  1445.  
  1446.     g->velInfoRect = g->pitchInfoRect;
  1447.     OffsetRect(&g->velInfoRect,0,kTextAllowance);
  1448.  
  1449.  
  1450.     /*
  1451.      * Window size
  1452.      * zoomed and unzoomed.
  1453.      */
  1454.     g->unzoomedBounds = ar;
  1455.     InsetRect(&g->unzoomedBounds,-kDocMargin,-kDocMargin);
  1456.     UnionRect(&ar,&g->keyInfoRect,&ar);
  1457.     g->zoomedBounds = ar;
  1458.     InsetRect(&g->zoomedBounds,-kDocMargin,-kDocMargin);
  1459.  
  1460.  
  1461.     /*
  1462.      * Transform the keycode list into
  1463.      * a code->position table.
  1464.      */
  1465.     for(i = 0; i< 256; i++)
  1466.         g->keyCodeToKeyPosition[i] = -1;
  1467.     for(i = 0; i < kKeyCount; i++)
  1468.         g->keyCodeToKeyPosition[dKeyPositionToKeyCode[i]] = i;
  1469.     g->lastKeyDown = 0;
  1470.  
  1471.     /*
  1472.      * Clear the keyboard masks
  1473.      */
  1474.     for(i = 15; i>=0; i--)
  1475.         g->keyMask[i] = 0;
  1476.     for(i = 0; i < kKeyCount; i++)
  1477.         g->keyPosition[i] = false;
  1478.  
  1479.     g->thisDoc = 0;
  1480.  
  1481.     /*
  1482.      * Sign in as a MIDI client.
  1483.      */
  1484.         {
  1485.         OSErr thisError;
  1486.         long midiClientID;
  1487.         MIDIPortParams mp;
  1488.  
  1489.         midiClientID = 'qwTu';
  1490. signin:
  1491.         thisError = MIDISignIn(midiClientID,0,nil,(StringPtr)0x910);
  1492.         if(thisError == midiDupIDErr)            /* client already logged in?        */
  1493.             {                                    /* no problem, bump to next ID.    */
  1494.             midiClientID++;
  1495.             goto signin;
  1496.             }
  1497.     
  1498.         if(thisError)
  1499.             Debugger();        /* should handle errors, you know??? */
  1500.     
  1501.         g->midiClientID = midiClientID;
  1502.  
  1503.         g->timePortID = 'time';
  1504.  
  1505.         mp.portID = g->timePortID;
  1506.         mp.portType = midiPortTypeTimeInv;
  1507.         mp.timeBase = 0;
  1508.         mp.offsetTime = 0;
  1509.         mp.readHook = 0;
  1510.         mp.refCon = (long) g;
  1511.         mp.initClock.sync = midiInternalSync;
  1512.         mp.initClock.curTime = 0;
  1513.         mp.initClock.format = midiFormatMSec;
  1514.         BlockMove("\ptime",&mp.name,32);
  1515.         thisError = MIDIAddPort(g->midiClientID,0,&g->timePort,&mp);        /* error?? */
  1516.         MIDIStartTime(g->timePort);
  1517.         }
  1518.  
  1519.  
  1520.     MIDIWakeUp(g->timePort,0,4,PeriodicThing);
  1521.     }
  1522.  
  1523. void Bootstrap()
  1524.     {
  1525.     long x;
  1526.  
  1527.     x = SndDispVersion(midiToolNum); 
  1528.     if(x == 0)
  1529.         {
  1530.         x = EasyDialogMessage(0,(StringPtr)0x910,"\pThis application requires the MIDI Manager",
  1531.                 kEasyDialogOkay);
  1532.         ExitToShell();
  1533.         }
  1534.  
  1535.  
  1536. /*** File Menu ***/
  1537.     InstallMenu("\pFile",nil,0);
  1538.     InstallMenuItem("\pNew/N",(void *)NewDoc,mNew);
  1539.     InstallMenuItem("\pOpen…/O",OpenDoc,mOpen);
  1540.     InstallMenuItem("\pClose/W",(void *)CloseDoc,-mClose);
  1541.     InstallMenuItem("\p(-",nil,0);
  1542.     InstallMenuItem("\pSave/S",(void *)SaveDoc,-mSave);
  1543.     InstallMenuItem("\pSave As…",(void *)SaveAsDoc,-mSaveAs);
  1544.     InstallMenuItem("\pQuit/Q",(void *)LetsQuit,0);
  1545.  
  1546. /*** Edit Menu ***/
  1547.     InstallEditMenu(nil,nil,nil,nil,nil);
  1548.  
  1549. /*** Qwertytunes Menu ***/
  1550.     InstallMenu("\pQwertytunes",nil,-mQwertytunes);
  1551.     InstallMenuItem("\pBackground Performance/B",BackgroundPlayDoc,-mBackgroundPlay);
  1552.     InstallMenuItem("\pSelect Instrument…/I",SelectInstrumentDoc,-mSelectInstrument);
  1553.     InstallMenuItem("\pSelect MIDI Channel…/L",SelectChannelDoc,-mSelectChannel);
  1554.     InstallMenuItem("\pSend MIDI Message…/M",MIDIMessageDoc,-mMIDIMessage);
  1555.     InstallMenuItem("\p(-",nil,0);
  1556.  
  1557.  
  1558.  
  1559.  
  1560.         {
  1561.         Str63 s;
  1562.  
  1563.         CopyPString(s,"\pAbout ");
  1564.         ConcatenatePStrings(s,(StringPtr)0x910);
  1565.         ConcatenatePStrings(s,"\p…");
  1566.         SetAbout(s,0,About);
  1567.         }
  1568.  
  1569.     SetMasterOpenDocProc(OpenDocSpec);
  1570.     SetMasterQuitAppProc(LetsQuit);
  1571.  
  1572.     InitVars();
  1573.     }
  1574.  
  1575. void Hatstrap()
  1576. /*
  1577.   * clean up
  1578.   */
  1579.     {
  1580.     MIDISignOut(g->midiClientID);
  1581.     }
  1582.